﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BomberManServer.Gaming.Time;
using BomberManServer.Utils;

namespace BomberManServer.Gaming.Object.Mapping.Items
{
    // Object managing the bomb and supervising the deflagration
    public class Bomb : Item
    {

        public Block block { get; private set; }
        private Trigger explosionTimer;
        public Coordinates coord { get; set; }
        private Logger.Handler log;
        // Deflag
        public List<Deflagration> deflags { get; set; }
        public UInt32 deflag_length { get; set; }
        private Trigger expandTimer;
        //private Trigger expireTimer;
        private UInt32 deflag_speed;
        public Avatar owner { get; private set; }
        public bool delayed { get; private set; }
        private Trigger delayTimer;
        public List<Block> deflagedBlock;
        private uint speed;
        private int count;
        private DateTime dtPlantBomb;
        private DateTime dtBombExplode;
        private DateTime dtDeflagExpire;

        public bool destructKick;

        public Bomb(Avatar avatar,Coordinates coord)
        {
            this.delayTimer = new Trigger((Constants.instance.actions_per_second * (int)Constants.instance.bomb_delay) - (int)avatar.bomb_delay, boomable);
            this.explosionTimer = new Trigger((Constants.instance.maxTimerBomb * Constants.instance.actions_per_second) + (int)avatar.bomb_delay, explode);
            this.owner = avatar;
            this.owner.game.scheduler.schedule(this.delayTimer);
            this.owner.game.scheduler.schedule(this.explosionTimer);
            this.owner.bombs.Add(this);
            this.coord = Coordinates.centered(coord);
            this.deflags = new List<Deflagration>();
            this.deflag_length = avatar.deflag_length;
            this.speed = avatar.deflag_length * Constants.instance.theoricPointsPerBlock / (Convert.ToUInt32(Constants.instance.actions_per_second / Constants.instance.bomb_Modifier) - Constants.instance.deflagStandstill);
            // <ATTENTION BIDOUILLE : probleme de gestion de temps  !!!!!!!!!!!!!!!>
            this.count = Constants.instance.actions_per_second / Constants.instance.bomb_Modifier;
            // <\ATTENTION BIDOUILLE : probleme de gestion de temps  !!!!!!!!!!!!!!!>
            this.log = owner.game.logger.createHandler("Bomb");
            this.delayed = false;
            this.block = owner.game.map.getBlockAt(this.coord);
            this.destructible = true;
            this.deflagedBlock = new List<Block>();
            this.dtPlantBomb = DateTime.Now;
        }

        public void explode()
        {
            // if delay is over
            if (delayed)
            {
                //explodeBombe
                boom();
                // send bomb explosion
                this.owner.game.sender.onBombExplode(this);
            }
        }

        public void chainReaction()
        {
            //explodeBombe
            boom();
            // send bomb explosion
            this.owner.game.sender.onBombExplode(this);
        }

        // Bomb explode
        public void boom()
        {
            
            this.block.item = Items.None.instance;
            this.log.information("Bomb explode on (" + coord.xBlock + ", " + coord.yBlock + ")");

            // triggers for deflags
            this.expandTimer = new Trigger(1, deflagPropagate);
            //this.expireTimer = new Trigger(Constants.instance.actions_per_second, deflagExpire);
            this.owner.game.scheduler.schedule(this.expandTimer);
            //this.owner.game.scheduler.schedule(this.expireTimer);

            this.owner.bombs.Remove(this);

            // Spawn deflags
            this.deflags.Add(new Deflagration(this, Direction.DOWN, this.speed));
            this.deflags.Add(new Deflagration(this, Direction.UP, this.speed));
            this.deflags.Add(new Deflagration(this, Direction.RIGHT, this.speed));
            this.deflags.Add(new Deflagration(this, Direction.LEFT, this.speed));
            
            // remove bomb trigger
            this.owner.game.scheduler.remove(this.explosionTimer);

            
            this.dtBombExplode = DateTime.Now;

        }

        public void boomable()
        {
            this.delayed = true;
            this.owner.game.scheduler.remove(this.delayTimer);
        }
        // Propagate the deflagration in all directions.
        public void deflagPropagate()
        {
            //this.log.information("Deflagration propagate for bomb on (" + coord.xBlock + ", " + coord.yBlock + ")");
            if (count == 0)
                this.deflagExpire();
            else
            {
                if (count > (int)Constants.instance.deflagStandstill)
                {
                    foreach (Deflagration deflag in deflags)
                    {
                        if (!deflag.ended)
                            deflag.propagate();
                    }
                }
                count--;
                expandTimer.reset();
            }
        }

        // THe deflagration has ended and it's remain on the map disappear
        public void deflagExpire()
        {
            this.log.information("Deflag expire for bomb on (" + coord.xBlock + ", " + coord.yBlock + ")");
            //foreach (Deflagration deflag in this.deflags)
            //{
            //    deflag.expire();
            //}
            //this.owner.game.scheduler.remove(this.expireTimer);
            //expireTimer.canceled = true;
            this.owner.game.scheduler.remove(this.expandTimer);
            foreach (Block block in deflagedBlock)
            {
                block.item = Items.None.instance;
            }
            deflagedBlock.Clear();

            this.dtDeflagExpire = DateTime.Now;

            log.error("Bomb planted at : " + dtPlantBomb.Hour + "h " + dtPlantBomb.Minute + "mn " + dtPlantBomb.Second + "s " + dtPlantBomb.Millisecond + "ms.");
            log.error("Bomb Exploded at : " + dtBombExplode.Hour + "h " + dtBombExplode.Minute + "mn " + dtBombExplode.Second + "s " + dtBombExplode.Millisecond + "ms.");
            log.error("Deflag expired at : " + dtDeflagExpire.Hour + "h " + dtDeflagExpire.Minute + "mn " + dtDeflagExpire.Second + "s " + dtDeflagExpire.Millisecond + "ms.");
           
        }

        // Function to get the longest deflag currently on the map for this bomb.
        public uint longestDeflag()
        {
            uint l_deflag = 0;
            foreach (Deflagration deflag in this.deflags)
                if (deflag.length > l_deflag)
                    l_deflag = deflag.length;
            return l_deflag;
        }

        //remove the block in the list when 2 deflag cross path
        public override Item actionDeflagration(Deflagration deflag)
        {
            this.chainReaction();
            return deflag;
        }

        public override void actionWalk(Block block, Avatar avatar)
        {
            if (!avatar.hasKick)
            {
                base.actionWalk(block, avatar);
                return;
            }
            Direction dir = avatar.coord.getDirectionTo(block.coordinates);
            Coordinates newCoord = kicked(dir);
            if(!newCoord.equalTheoric(this.coord))
            {
                this.owner.game.sender.onBombKick(dir,this.coord);
                this.coord = newCoord;
                reattach(this.owner.game.map.getBlockAt(this.coord));
                base.actionWalk(block, avatar);
            }
        }

        public void reattach(Block newBlock)
        {
            this.block.item = Items.None.instance;
            this.block = newBlock;
            newBlock.item = this;
        }

        public Coordinates kicked(Direction dir)
        {
            Coordinates newCoord = this.coord;
            switch (dir)
            {
                case Direction.UP:
                    if (this.owner.game.map.getBlockAt(Coordinates.fromBlock(this.coord.xBlock, this.coord.yBlock - 1)).item.free)
                        newCoord = Coordinates.centered(Coordinates.fromBlock(this.coord.xBlock, this.coord.yBlock - 1));
                    break;
                case Direction.DOWN:
                    if(this.owner.game.map.getBlockAt(Coordinates.fromBlock(this.coord.xBlock,this.coord.yBlock+1)).item.free)
                        newCoord = Coordinates.centered(Coordinates.fromBlock(this.coord.xBlock, this.coord.yBlock + 1));
                    break;
                case Direction.RIGHT:
                    if(this.owner.game.map.getBlockAt(Coordinates.fromBlock(this.coord.xBlock+1,this.coord.yBlock)).item.free)
                        newCoord = Coordinates.centered(Coordinates.fromBlock(this.coord.xBlock + 1, this.coord.yBlock));
                    break;
                case Direction.LEFT:
                    if(this.owner.game.map.getBlockAt(Coordinates.fromBlock(this.coord.xBlock-1,this.coord.yBlock)).item.free)
                        newCoord = Coordinates.centered(Coordinates.fromBlock(this.coord.xBlock - 1, this.coord.yBlock));
                    break;
            }
            return newCoord;
        }


    }
}
